package org.javanum.number;

import org.junit.Test;

import static org.javanum.number.TestUtils.assertComplexEquality;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;

/**
 * Copyright 2010 Scott Fines
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p/>
 * Author: Scott Fines
 * Date: Mar 7, 2010
 */
public class ComplexTestSpecialValues {
    private static final double DELTA = Math.pow(1.0,-10);

    /*Add tests*/

    @Test
	public void testAddComplexInfinity(){
        /*
        Tests that adding Complex infinity to any Complex z
        results in Complex Infinity
         */
		Complex c = getOneNumber();
		assumeNotNull(c);
        assumeNotNaN(c);

		assertTrue("Adding COMPLEX_INFINITY does " +
                "not result in COMPLEX_INFINITY",
				Complex.COMPLEX_INFINITY.add(c).
                        equals(Complex.COMPLEX_INFINITY));
		assertTrue("Adding COMPLEX_INFINITY does not result " +
                "in COMPLEX_INFINITY",
				c.add(Complex.COMPLEX_INFINITY).
                        equals(Complex.COMPLEX_INFINITY));
	}

    @Test
    public void testAddRealInfinity() throws Exception {
        /*
            Tests that adding
        Real Infinity(Positive and Negative) to any Complex
        z results in Complex inifinity.
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);

        Complex sum = c.add(Real.POSITIVE_INFINITY);
        assertTrue(String.format("Adding Real.POSITIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(sum));

        Complex sumNeg = c.add(Real.NEGATIVE_INFINITY);
        assertTrue(String.format("Adding Real.NEGATIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(sumNeg));

    }

    @Test
	public void testAddComplexZero(){
        /*
        Tests that adding 0 to any complex z results in z
         */
		Complex c = getOneNumber();
		assumeNotNull(c);
        assumeTrue(!Complex.isNaN(c));
        assumeTrue(!Complex.isInfinite(c));

		Complex result = c.add(Complex.ZERO);

		assertEquals("Real parts do not match: "+
				result.realPart().value()+"!="+c.realPart().value(),
                result.realPart().value(),c.realPart().value(),DELTA);
		assertEquals("Imaginary parts do not match: "+
				result.imaginaryPart().value()+"!="+
				c.imaginaryPart().value(),result.imaginaryPart().value(),
                c.imaginaryPart().value(),DELTA);
	}

    @Test
    public void testAddRealZero() throws Exception {
        /*
        Tests that adding 0 to any complex z results in z
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex add = c.add(Real.ZERO);

        assertEquals(String.format("z+0!=z" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                c.real,add.real,DELTA);
        assertEquals(String.format("z+0!=z" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                c.imaginary,add.imaginary,DELTA);
    }

    @Test
    public void testAddComplexNaN(){
        /*
        Tests that adding Complex.NaN to any Complex z
        results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex d = c.add(Complex.NaN);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(d.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(d.imaginary));

        Complex e = Complex.NaN.add(c);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(e.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(e.imaginary));
    }

    @Test
    public void testAddRealNaN() throws Exception {
        /*
        Tests that adding Complex.NaN to any Complex z
        results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex sum = c.add(Real.NaN);
        assertEquals(String.format("z+Real.NaN!=NaN" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                Double.NaN,sum.real,DELTA);
        assertEquals(String.format("z+Real.NaN!=NaN" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                Double.NaN,sum.imaginary,DELTA);
    }

/*---------------------------------------------------------------*/
    /*Subtract tests*/

    @Test
	public void testSubtractComplexInfinity(){
        /*
        Tests that subtracting Complex infinity from any
        Complex z results in Complex Infinity
         */
		Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);

		assertTrue("Subtracting COMPLEX_INFINITY does not " +
                "result in COMPLEX_INFINITY",
				c.subtract(Complex.COMPLEX_INFINITY).
                        equals(Complex.COMPLEX_INFINITY));
		assertTrue("Subtracting from COMPLEX_INFINITY does " +
                "not result in COMPLEX_INFINITY",
				Complex.COMPLEX_INFINITY.subtract(c).
                        equals(Complex.COMPLEX_INFINITY));
	}

    @Test
    public void testSubtractRealInfinity() throws Exception {
        /*
            Tests that subtracting Real Infinity(Positive and Negative)
             to any Complex z results in Complex infinity.
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);

        Complex diff = c.subtract(Real.POSITIVE_INFINITY);
        assertTrue(String.format("Subtracting Real.POSITIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(diff));

        Complex sumNeg = c.add(Real.NEGATIVE_INFINITY);
        assertTrue(String.format("Subtracting Real.NEGATIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(sumNeg));
    }

    @Test
	public void testSubtractComplexZero(){
        /*
        Tests that subtracting 0 from any Complex z results
        in z
         */
		Complex c = getOneNumber();
		assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

		Complex result = c.subtract(Complex.ZERO);

		assertEquals("Real parts do not match: "+
				result.realPart().value()+"!="+
                c.realPart().value(),result.realPart().value(),
                c.realPart().value(),DELTA);
		assertEquals("Imaginary parts do not match: "+
				result.imaginaryPart().value()+"!="+
				c.imaginaryPart().value(),result.imaginaryPart().value(),
                c.imaginaryPart().value(),DELTA);
	}

    @Test
    public void testSubtractRealZero() throws Exception {
          /*
        Tests that subtracting 0 from any complex z results in z
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex diff = c.subtract(Real.ZERO);

        assertEquals(String.format("z-0!=z" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                c.real,diff.real,DELTA);
        assertEquals(String.format("z-0!=z" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                c.imaginary,diff.imaginary,DELTA);
    }

    @Test
    public void testSubtractComplexNaN(){
        /*
        Tests that subtracting Complex.NaN from any number
        results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex d = c.subtract(Complex.NaN);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(d.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(d.imaginary));

        Complex e = Complex.NaN.subtract(c);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(e.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(e.imaginary));
    }

    @Test
    public void testSubtractRealNaN() throws Exception {
        /*
        Tests that subtracting Real.NaN from any Complex z
        results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex diff = c.subtract(Real.NaN);
        assertEquals(String.format("z-Real.NaN!=NaN" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                Complex.NaN.real,diff.real,DELTA);
        assertEquals(String.format("z-Real.NaN!=NaN" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                Complex.NaN.imaginary,diff.imaginary,DELTA);
    }

/*---------------------------------------------------------------*/
    /*Multiply tests*/
    @Test
	public void testMultiplyComplexInfinity(){
        /*
        Tests that multiplying by ComplexInfinity returns
        ComplexInfinity
         */
		Complex c = getOneNumber();
		assumeTrue(c!=null);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

		Complex result = Complex.COMPLEX_INFINITY.multiply(c);

		assertEquals("Real parts do not match: "+
				result.realPart().value()+"!="+
                Complex.COMPLEX_INFINITY.realPart().value(),
				result.realPart().value(),
				Complex.COMPLEX_INFINITY.realPart().value(),
				DELTA);
		assertEquals("Imaginary parts do not match: "+
				result.imaginaryPart().value()+"!="+
                Complex.COMPLEX_INFINITY.imaginaryPart().value(),
				result.imaginaryPart().value(),
				Complex.COMPLEX_INFINITY.imaginaryPart().value(),
				DELTA);
	}

    @Test
    public void testMultiplyRealInfinity() throws Exception {
        /*
            Tests that multiplying Real Infinity(Positive and Negative)
             by any Complex z results in Complex infinity.
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);

        Complex multi1 = c.multiply(Real.POSITIVE_INFINITY);
        assertTrue(String.format("Multiplying Real.POSITIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(multi1));

        Complex multi2 = c.add(Real.NEGATIVE_INFINITY);
        assertTrue(String.format("Multiplying Real.NEGATIVE_INFINITY does" +
                "not result in COMPLEX_INFINITY"),
                Complex.COMPLEX_INFINITY.equals(multi2));
    }

    @Test
	public void testMultiplyComplexZero(){
        /*
        Tests that Multiplying by 0 results in 0
         */
		Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

		Complex result = Complex.ZERO.multiply(c);

		assertEquals("Real parts do not match: "+
				result.realPart().value()+"!="+
                Complex.ZERO.realPart().value(),
				result.realPart().value(),
				Complex.ZERO.realPart().value(),
				DELTA);
		assertEquals("Imaginary parts do not match: "+
				result.imaginaryPart().value()+"!="+
                Complex.ZERO.imaginaryPart().value(),
				result.imaginaryPart().value(),
				Complex.ZERO.imaginaryPart().value(),
				DELTA);
	}

    @Test
    public void testMultiplyRealZero() throws Exception {
          /*
        Tests that multiplying and Complex z by 0
        results in 0
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex mult = c.multiply(Real.ZERO);

        assertEquals(String.format("z*0!=0" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                0d,mult.real,DELTA);
        assertEquals(String.format("z*0!=0" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                c.imaginary<0?-0d:0d,mult.imaginary,DELTA);
    }

    @Test
    public void testMultiplyComplexNaN(){
        /*
        Tests that Multiplying by Complex.NaN results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex d = c.multiply(Complex.NaN);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(d.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(d.imaginary));

        Complex e = Complex.NaN.multiply(c);
        assertTrue("Real parts do not match. ",
                ((Double)Double.NaN).equals(e.real));
        assertTrue("Imaginary parts do not match. ",
                ((Double)Double.NaN).equals(e.imaginary));
    }

    @Test
    public void testMultiplyRealNaN() throws Exception {
         /*
        Tests that multiplying Real.NaN by any Complex z
        results in Complex.NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

        Complex diff = c.multiply(Real.NaN);
        assertEquals(String.format("z*Real.NaN!=NaN" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                Complex.NaN.real,diff.real,DELTA);
        assertEquals(String.format("z*Real.NaN!=NaN" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                Complex.NaN.imaginary,diff.imaginary,DELTA);
    }

/*---------------------------------------------------------------*/
    /*Divide tests*/
    @Test(expected=ArithmeticException.class)
	public void testDivideByComplexZero(){
        /*
        Tests that dividing by 0 throws an exception
         */
		Complex c = getOneNumber();
		assumeNotNull(c);

		c.divide(Complex.ZERO);
	}

    @Test(expected=ArithmeticException.class)
    public void testDivideByRealZero() throws Exception {
       /*
        Tests that dividing by 0 throws an exception
         */
		Complex c = getOneNumber();
		assumeNotNull(c);

		c.divide(Real.ZERO); 
    }

    @Test
    public void testDivideByComplexNaN(){
        /*
        Tests that dividing by NaN results in NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeTrue(!Complex.isNaN(c));
        assumeTrue(!Complex.isInfinite(c));

        assertTrue("dividing by NaN does not result " +
            "in Complex.NaN",
                Complex.NaN.equals(c.divide(Complex.NaN)));
    }

    @Test
    public void testDivideByRealNaN() throws Exception {
        /*
        Tests that dividing by NaN results in NaN
         */
        Complex c = getOneNumber();
        assumeNotNull(c);
        assumeTrue(!Complex.isNaN(c));
        assumeTrue(!Complex.isInfinite(c));

        assertTrue("dividing by NaN does not result " +
            "in Complex.NaN",
                Complex.NaN.equals(c.divide(Real.NaN)));
    }

    @Test
	public void testDivideByComplexInfinity(){
        /*
        Tests that dividing by ComplexInfinty results in
        0
         */
		Complex c = getOneNumber();
        assumeTrue(!Complex.isNaN(c));
        assumeTrue(!Complex.isInfinite(c));

		Complex result = c.divide(Complex.COMPLEX_INFINITY);

		assertEquals("Real parts do not match: "+
                        result.realPart().value()+"!="+ 0.0,0.0,
					result.realPart().value(),
					DELTA);
		assertEquals("Imaginary parts do not match: "+
                        result.imaginaryPart().value()+"!="+0.0,0.0,
					result.imaginaryPart().value(),
					DELTA);
	}

    @Test
    public void testDivideByRealInfinity() throws Exception {
        /*
        Tests that dividing by Real.POSITIVE_INFINITY results in
        0, and that dividing by Real.NEGATIVE_INFINITY results in
        -0
         */
		Complex c = getOneNumber();
        assumeNotNaN(c);
        assumeNotComplexInfinity(c);

		Complex posResult = c.divide(Real.POSITIVE_INFINITY);
        assertEquals(String.format("z/Real.POSITIVE_INFINTY!=0" +
                "--real parts do not match%n" +
                "z=%1$s",c),
                0d,posResult.real,DELTA);
        assertEquals(String.format("z/Real.POSITIVE_INFINTY!=0" +
                "--imaginary parts do not match%n" +
                "z=%1$s",c),
                c.imaginary<0?-0d:0d,posResult.real,DELTA);

        Complex negResult = c.divide(Real.NEGATIVE_INFINITY);
        assertEquals(String.format("z/Real.NEGATIVE_INFINITY!=0" +
                        "--real parts do not match%n" +
                        "z=%1$s",c),
                        0d,negResult.real,DELTA);
                assertEquals(String.format("z/Real.POSITIVE_INFINTY!=0" +
                        "--imaginary parts do not match%n" +
                        "z=%1$s",c),
                        c.imaginary<0?-0d:0d,negResult.real,DELTA);
    }

    /*---------------------------------------------------------------*/
    /*Conjugate methods */

    @Test
    public void testConjugateComplexInfinity() throws Exception {
        /*
        Tests that the conjugate of ComplexInfinity is ComplexInfinity
         */
        Complex c = Complex.COMPLEX_INFINITY;

        Complex conj = c.conjugate();
        assertTrue(String.format("conjugate(infinity)!=infinity%n" +
                "conjugate(infinity)=%1$s",conj),c.equals(conj));
    }

    @Test
    public void testConjugateNaN() throws Exception {
        /*
        Tests that the conjugate of Complex.NaN is Complex.NaN
         */
        Complex c = Complex.NaN;

        Complex conj = c.conjugate();
        assertTrue(String.format("conjugate(NaN)!=NaN%n" +
                "conjugate(NaN)=%1$s",conj),c.equals(conj));
    }

    @Test
    public void testConjugateZero() throws Exception {
        /*
        Tests that the conjugate of 0 is 0 in
        floating point. That is, 0-i0 is correct
         */
        Complex c = Complex.ZERO;


        Complex conj = c.conjugate();
        assertEquals(String.format("conjugate(0)!=0" +
                "--real parts do not match%n" +
                "conjugate(0)=%1$s",conj),
                0d,conj.real,DELTA);
        assertEquals(String.format("conjugate(0)!=0" +
                "--imaginary parts do not match%n" +
                "conjugate(0)=%1$s",conj),
                -0d,conj.real,DELTA);
    }


/*---------------------------------------------------------------*/
    /*modulus methods */

    @Test
    public void testModulusComplexInfinity() throws Exception {
        /*
        Tests that the modulus of ComplexInfinity is ComplexInfinity
         */
        Complex c = Complex.COMPLEX_INFINITY;
        double modulus = c.modulus();

        assertEquals("modulus(infinity)!=infinity",
                Double.POSITIVE_INFINITY,modulus,DELTA);
    }

    @Test
    public void testModulusZero() throws Exception {
        /*
        Tests that the modulus of 0 is 0
         */
        Complex c = Complex.ZERO;
        double modulus = c.modulus();

        assertEquals("modulus(0)!=0",0d,modulus,DELTA);
    }

    @Test
    public void testModulusNaN() throws Exception {
        /*
        Tests that the modulus of Complex.NaN is Complex.NaN
         */
        Complex c = Complex.NaN;
        double modulus = c.modulus();

        assertEquals("modulus(NaN)!=NaN",Double.NaN,modulus,DELTA); 
    }

/*---------------------------------------------------------------*/
    /*Hashcode methods */

    @Test
    public void testHashCodeOfComplexInfinity() throws Exception {
        //TODO -sf- implement!
    }

 /*---------------------------------------------------------------*/
    /*polarForm() methods */

    @Test
    public void testPolarFormRealPartZero() throws Exception {
        /*
        Tests that the polar form of a number is correct if
        the real part of that Complex number is zero
         */
        Complex z = Complex.valueOf(0,2);

        PolarComplex p = z.polarForm();

        PolarComplex correct = PolarComplex.valueOf(z.modulus(),Math.PI/2);

        assertComplexEquality("polar form fails",correct,p,TestUtils.DELTA);

    }

    @Test
    public void testPolarFormOfComplexInfinity() throws Exception {
        /*
        Tests that the polar form of ComplexInfinity is
        PolarComplex.ComplexInfinity
         */

        Complex inf = Complex.COMPLEX_INFINITY;
        PolarComplex polarForm = inf.polarForm();
        PolarComplex correct = PolarComplex.COMPLEX_INFINITY;

        assertComplexEquality("polar form of Complex.ComplexInfinity" +
                "!=PolarComplex.ComplexInfinity",
                correct, polarForm,DELTA);
    }

    @Test
    public void testPolarFormOfNaN() throws Exception {
        /*
        Tests that the polar form of Complex.NaN is PolarComplex.NaN
         */

        Complex nan = Complex.NaN;
        PolarComplex polarForm = nan.polarForm();
        PolarComplex correct = PolarComplex.NaN;

        assertComplexEquality("polar form of Complex.NaN!=PolarComplex.NaN",
                    correct,polarForm,DELTA);
    }

    @Test
    public void testPolarFormOfZero() throws Exception {
        /*
        Tests that the polar form of Complex.ZERO is PolarComplex.ZERO
         */
        Complex zero = Complex.ZERO;
        PolarComplex polarForm = zero.polarForm();
        PolarComplex correct = PolarComplex.ZERO;

        assertComplexEquality("polar form of Complex.ZERO!=PolarComplex.ZERO",
                            correct,polarForm,DELTA);
    }

    @Test
    public void testPolarFormWhenRealZero() throws Exception {
        /*
        Tests that (a+i0).polarForm()=abs(a)e^(a<0?-PI:0)i
         */

        Complex neg = Complex.valueOf(-1,0);
        PolarComplex polarNeg = neg.polarForm();

        PolarComplex correctNeg = PolarComplex.valueOf(1,-Math.PI);

        assertComplexEquality("(-1+0i).polarForm()!=1e^(-PI)i",
                                    correctNeg,polarNeg,DELTA);

        Complex pos = Complex.valueOf(1,0);
        PolarComplex polarPos = pos.polarForm();

        PolarComplex correctPos = PolarComplex.valueOf(1,0);
        assertComplexEquality("(1+0i).polarForm()!=1e^(0i)",
                                correctPos,polarPos,DELTA);
    }

    /*---------------------------------------------------------------*/
    /*Helper methods */

    private static Complex getOneNumber(){
        return Complex.valueOf(10,12);
    }

    private static void assumeNotNaN(Complex z){
        assumeTrue(!Complex.isNaN(z));
    }

    private void assumeNotComplexInfinity(Complex c) {
        assumeTrue(!Complex.isInfinite(c));
    }
}
